package com.credithc.eagleeye.plugins.feign;
import com.credithc.eagleeye.plugins.util.ClassUtil;
import feign.Client;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;

import java.util.HashMap;
import java.util.Map;
/**
 * 描 述: <br/>
 * 作 者: zhangjin<br/>
 * 创 建：2019年11月01日<br/>
 * 版 本：v1.0.0<br>
 * <p>
 * 历 史: (版本) 作者 时间 注释 <br/>
 */
public class EagleEyeFeignContext extends FeignContext {
    private final FeignContext delegate;
    private final BeanFactory beanFactory;
    private CachingSpringLoadBalancerFactory cachingSpringLoadBalancerFactory;
    private Object springClientFactory;

    public EagleEyeFeignContext(FeignContext delegate, BeanFactory beanFactory) {
        this.delegate = delegate;
        this.beanFactory = beanFactory;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getInstance(String name, Class<T> type) {
        T bean = this.delegate.getInstance(name, type);
        return (T) this.wrap(bean);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Map<String, T> getInstances(String name, Class<T> type) {
        Map<String, T> instances = this.delegate.getInstances(name, type);
        if (instances == null) {
            return null;
        }
        Map<String, T> convertedInstances = new HashMap<>();
        for (Map.Entry<String, T> entry : instances.entrySet()) {
            convertedInstances.put(entry.getKey(), (T) this.wrap(entry.getValue()));
        }
        return convertedInstances;
    }

    Object wrap(Object bean) {
        if (bean instanceof Client && !(bean instanceof EagleEyeFeignClient)) {
            if (ClassUtil.ribbonPresent && bean instanceof LoadBalancerFeignClient && !(bean instanceof EagleEyeLoadBalancerFeignClient)){
                LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
                return  new EagleEyeLoadBalancerFeignClient((Client)wrap(client.getDelegate()), getCachingSpringLoadBalancerFactory(), (SpringClientFactory)getSpringClientFactory());
            } else if (ClassUtil.ribbonPresent && bean instanceof EagleEyeLoadBalancerFeignClient) {
                return bean;
            }
            return new EagleEyeFeignClient((Client) bean);
        }
        return bean;
    }

    private CachingSpringLoadBalancerFactory getCachingSpringLoadBalancerFactory() {
        if (this.cachingSpringLoadBalancerFactory == null) {
            this.cachingSpringLoadBalancerFactory = this.beanFactory
                    .getBean(CachingSpringLoadBalancerFactory.class);
        }
        return this.cachingSpringLoadBalancerFactory;
    }

    private Object getSpringClientFactory() {
        if (this.springClientFactory == null) {
            this.springClientFactory = this.beanFactory
                    .getBean(SpringClientFactory.class);
        }
        return this.springClientFactory;
    }
}
